package config

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/cihub/seelog"
	"github.com/spf13/viper"
	"io"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"syscall"
)

var lineEnd string
var TddhHome string

func init() {
	if runtime.GOOS == "windows" {
		lineEnd = "\r\n"
	} else {
		lineEnd = "\n"
	}
	tddhPath, _ := os.Executable()

	TddhHome, _ = filepath.Split(tddhPath)
}

func checkHostFile(hostList *string) bool {

	info, err := os.Stat(*hostList)
	if err != nil {
		return false
	}

	t := info.Sys().(*syscall.Stat_t).Mtim

	mtime := filepath.Join(TddhHome, ".cache/.mtime")
	host := filepath.Join(TddhHome, ".cache/.hosts.toml")

	if !checkFileIsExist(host) || !checkFileIsExist(mtime) {
		err = os.MkdirAll(filepath.Join(TddhHome, ".cache"), os.ModePerm)
		check(err)

		err = os.WriteFile(mtime, []byte(strconv.FormatInt(t.Nano(), 10)), 0666)
		check(err)
		return false
	}

	t1, _ := os.ReadFile(mtime)
	if strconv.FormatInt(t.Nano(), 10) == string(t1) {
		return true
	}

	err = os.WriteFile(mtime, []byte(strconv.FormatInt(t.Nano(), 10)), 0666)
	check(err)

	return false

}

func HostFmt(hostList *string) (configMap *viper.Viper) {
	defer seelog.Flush()
	seelog.Trace("unmarshal host file: ", hostList)
	var filename = filepath.Join(TddhHome, ".cache/.hosts.toml")
	var f *os.File

	if checkHostFile(hostList) {
		return unmarshal(filename)
	}

	fi, err := os.Open(*hostList)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return
	}

	defer func(fi *os.File) {
		errClose := fi.Close()
		if errClose != nil {
			_ = seelog.Warn(fi.Name(), " close failed.")
		}
	}(fi)

	if checkFileIsExist(filename) {
		f, err = os.OpenFile(filename, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666)
	} else {
		err = os.MkdirAll(TddhHome+".cache/"+GlobalTimestamp, os.ModePerm)
		check(err)
		f, err = os.Create(filename)
	}
	check(err)

	br := bufio.NewReader(fi)
	var groupName string
	var hostVar []string
	var hostBuffer bytes.Buffer

	for {
		line, _, c := br.ReadLine()
		if c == io.EOF {
			break
		}

		if strings.Compare(string(line), "") == 0 || strings.Compare(string(line), lineEnd) == 0 {
			continue
		}

		regexpBool, _ := regexp.Match(`\[\[\w.*`, line) //判断是否为组名：[[groupName]]
		//如果是组名则存入变量
		if regexpBool {
			groupName = string(line)
			//fmt.Println("groupName ", groupName)
			goto write
		}

		//将变量按空格分隔为字符串切片
		hostVar = HostVarSpilt(string(line))

		if strings.HasPrefix(hostVar[0], "#") {
			continue
		}

		//hostIp = lacia.SplitByManyStrWith(hostVar[0], []string{"\"", "="})
		if strings.Contains(hostVar[0], "]") {
			//fmt.Println("执行合并")
			hostBuffer.WriteString(contHostIp(hostVar, groupName))
			goto write
		}

		hostBuffer.WriteString(groupName)
		hostBuffer.WriteString(lineEnd)

		for _, v := range hostVar {
			hostBuffer.WriteString(v)
			hostBuffer.WriteString(lineEnd)
		}

		hostBuffer.WriteString("host_group=")
		hostBuffer.WriteString("\"" + groupName[2:len(groupName)-2] + "\"")
		hostBuffer.WriteString(lineEnd)

	write:
		_, err = io.WriteString(f, hostBuffer.String())
		check(err)
		hostBuffer.Reset()

	}

	return unmarshal(filename)
}

func unmarshal(file string) (configMap *viper.Viper) {
	configs := viper.New()
	configs.SetConfigFile(file)
	readErr := configs.ReadInConfig()
	if readErr != nil {
		fmt.Println(readErr)
		return
	}
	var c map[string]interface{}
	_ = configs.Unmarshal(&c)

	configMap = configs
	return configMap
}

func mapToJson(m interface{}) {
	jsonTxt, err := json.Marshal(m)
	if err != nil {
		log.Fatalf(err.Error())
	}
	fmt.Println(string(jsonTxt))
}

func HostVarSpilt(stringList string) (s []string) {
	s = strings.Fields(stringList)
	return
}

func checkFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); err != nil {
		exist = false
	}
	return exist
}

func DeleteCacheFile() {
	var filename = TddhHome + ".cache/" + GlobalTimestamp
	if checkFileIsExist(filename) {
		_ = os.RemoveAll(filename)
	}
}

func contHostIp(hosts []string, groupName string) string {
	host := hosts[0]
	var newHosts [2]bytes.Buffer
	arrHostIndex0 := strings.Index(host, "[")
	arrHostIndex1 := strings.Index(host, ":")
	arrHostIndex2 := strings.Index(host, "]")
	if arrHostIndex0 != -1 {
		/*IP前缀*/
		startStr := host[:arrHostIndex0]

		/*IP数字部分起始值*/
		startNum, err := strconv.Atoi(host[arrHostIndex0+1 : arrHostIndex1])
		if err != nil {
			log.Fatalf("主机列表格式错误 %v ", err)
		}

		/*IP数字部分结束值*/
		stopNum, err := strconv.Atoi(host[arrHostIndex1+1 : arrHostIndex2])
		if err != nil {
			log.Fatalf("主机列表格式错误 %v ", err)
		}

		for _, v := range hosts[1:] {
			newHosts[0].WriteString(v)
			newHosts[0].WriteString(lineEnd)
		}
		newHosts[0].WriteString("host_group=")
		newHosts[0].WriteString("\"" + groupName[2:len(groupName)-2] + "\"")
		newHosts[0].WriteString(lineEnd)

		/*生成主机列表*/
		for nh := startNum; nh <= stopNum; nh++ {
			newHosts[1].WriteString(groupName)
			newHosts[1].WriteString(lineEnd)

			newHosts[1].WriteString(startStr)
			newHosts[1].WriteString(strconv.Itoa(nh))
			newHosts[1].WriteString("\"")

			newHosts[1].WriteString(lineEnd)
			newHosts[1].WriteString(newHosts[0].String())
		}

	}
	return newHosts[1].String()
}

func matchGroup(group []string, s string) bool {
	if len(group) == 0 {
		return true
	}

	for _, g := range group {
		i := strings.Compare(g, s)
		if i == 0 {
			return true
		}
	}
	return false
}

func setHostVars() {
	var varMap = make(map[string]interface{})
	if _, tomlErr := toml.DecodeFile(filepath.Join(TddhHome, ".cache/.hosts.toml"), &varMap); tomlErr != nil {
		_ = seelog.Error("set host var: ", tomlErr.Error())
		return
	}

	for g, valC := range varMap {
		for _, v := range valC.([]map[string]interface{}) {
			k := g + "." + v["host_ip"].(string)
			HostVars[k] = v
		}
	}
	defer seelog.Flush()
}

func unConf(m map[string]interface{}) {
	for keyC, valC := range m {
		fmt.Println(reflect.ValueOf(keyC), reflect.TypeOf(valC).String(), ":::", reflect.ValueOf(valC))
		if reflect.TypeOf(valC).Kind() == reflect.String {
			//fmt.Println(reflect.ValueOf(keyC), reflect.TypeOf(valC).String(), ":::", reflect.ValueOf(valC))
		} else if reflect.TypeOf(valC).Kind() == reflect.Map {
			//fmt.Println("kind: ", reflect.TypeOf(valC).Kind())
			//fmt.Println(len(reflect.TypeOf(valC).String()))
			unConf(m[keyC].(map[string]interface{}))
			// fmt.Println("======================")
			// //fmt.Println(m[keyC])
			// fmt.Println(reflect.ValueOf(keyC), reflect.TypeOf(valC).string, ":::", reflect.ValueOf(valC))
			// fmt.Println("======================")
		} else if reflect.TypeOf(valC).Kind() == reflect.Slice {
			//fmt.Println("array: ", reflect.ValueOf(valC))
			//fmt.Println("slicesss ", viper.GetStringSlice(reflect.ValueOf(keyC).String()))
		}
	}
}
